package org.batatasframework.immadiate;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;

import org.batatasframework.common.ParameterReader;
import org.batatasframework.common.ParameterReader.SubparameterReader;
import org.batatasframework.logger.FieldAmendmentRecordHandler;
import org.batatasframework.logger.FieldAmendmentRecordHandler.FieldAmendmentRecordBuilder;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import cn.bonoon.entities.ImmediatePlanEntity;
import cn.bonoon.entities.ImmediateTaskEntity;
import cn.bonoon.entities.ImmediateTaskField;
import cn.bonoon.entities.ImmediateTaskLog;
import cn.bonoon.entities.ImmediateTaskMaterial;
import cn.bonoon.entities.ImmediateTaskStatus;
import cn.bonoon.entities.ImmediateTaskStatusLog;
import cn.bonoon.kernel.io.FileInfo;
import cn.bonoon.kernel.io.FileManager;
import cn.bonoon.kernel.io.StorageFile;
import cn.bonoon.kernel.io.StorageMultipartFile;
import cn.bonoon.kernel.support.IOperator;

public abstract class ImmediateTaskHandler<T extends ImmediateTaskEntity> {
	
	public final static String VAR_TASK_INFO = "taskinfo";
	public final static String VAR_TASK_MATERIALS = "materials";
	
	public final static String STATUS_EDITABLE = "taskeditable";
	
	public final static String POST_TASK_ID = "001_0";
	public final static String POST_TASK_MATERIAL = "001_1";
	public final static String POST_TASK_MATERIAL_ID = "001_1_0_";
	public final static String POST_TASK_MATERIAL_NAME = "001_1_1_";
	public final static String POST_TASK_MATERIAL_FILE = "001_1_2_";
	public final static String POST_TASK_REMARK = "001_2";
	
	public final static String SAVE_FILE_DIR = "immediate";
	
//	protected EntityManager entityManager;
//	protected FileManager fileManager;
	protected ImmadiatePlanDefinition definition;
	protected final Class<T> taskClass;
	
	public ImmediateTaskHandler(Class<T> taskClass){
		this.taskClass = taskClass;
	}
	
	ImmediateTaskHandler<T> initialize(/*EntityManager entityManager, *//*FileManager fileManager, */ImmadiatePlanDefinition definition) {
//		this.entityManager = entityManager;
		this.definition = definition;
//		this.fileManager = fileManager;
		
		return this;
	}
	
	protected abstract T create(IOperator operator, ParameterReader parameter);
	
	T load(IOperator operator, ParameterReader parameter, EntityManager entityManager, Long id){
		T task = entityManager.find(taskClass, id);
		Assert.notNull(task, "无法加载任务对象[id:" + id + "]！");
		
		return task;
	}
	
	protected abstract void beforeSave(IOperator operator, EntityManager entityManager, ParameterReader parameter, T entity);
	protected abstract void beforeUpdate(IOperator operator, 
			EntityManager entityManager, ParameterReader parameter, T entity,
			FieldAmendmentRecordHandler<ImmediateTaskField> fieldRecordLogger);

	protected abstract void afterSave(IOperator operator, EntityManager entityManager, ParameterReader parameter, T entity);
	protected abstract void afterUpdate(IOperator operator, 
			EntityManager entityManager, ParameterReader parameter, T entity,
			FieldAmendmentRecordHandler<ImmediateTaskField> fieldRecordLogger);

	void update(EntityManager entityManager, FileManager fileManager, IOperator user, ParameterReader parameter) throws Exception{
		Long id = parameter.getLong(POST_TASK_ID);
		T task;
		Date now = new Date();
		String logContent = "";
		
		List<ImmediateTaskMaterial> newMaterials = new ArrayList<>();
		
		if(null != id && id > 0){
			task = load(user, parameter, entityManager, id);
			
			ImmadiateScheduleHandler.checkEditable(definition.getKey(), user, task, now);
			ImmediateTaskStatus oldStatus = task.getStatus();
			logContent = "[修改任务]";

			FieldAmendmentRecordHandler<ImmediateTaskField> fieldRecordLogger = new FieldAmendmentRecordHandler<>(
					new FieldAmendmentRecordBuilder<ImmediateTaskField>(){
						
						@Override
						public ImmediateTaskField build() {
							return new ImmediateTaskField();
						}
						
					});

			task.setRemark(fieldRecordLogger.parseValue("remark", task.getRemark(), parameter.getString(POST_TASK_REMARK)));
			beforeUpdate(user, entityManager, parameter, task, fieldRecordLogger);
			
			if(task.getStatus() == ImmediateTaskStatus.UNACCEPTED){
				task.setStatus(ImmediateTaskStatus.STARTED);
			}
			entityManager.merge(task);
			afterUpdate(user, entityManager, parameter, task, fieldRecordLogger);
			
			//处理上传文件
			List<ImmediateTaskMaterial> oldMaterials = task.getMaterials();
			for(SubparameterReader materialReader : parameter.parameters(POST_TASK_MATERIAL)){
				Long mid = materialReader.getLong(POST_TASK_MATERIAL_ID);
				ImmediateTaskMaterial oldMaterial = findMaterial(oldMaterials, mid);
				if(null != oldMaterial){		
					MultipartFile file = materialReader.getFile(POST_TASK_MATERIAL_FILE);
					String name = null;
					if(null != file){
						name = materialReader.getString(POST_TASK_MATERIAL_NAME, file.getOriginalFilename());
						
						String oldPath = oldMaterial.getPath();
						storageMaterial(fileManager, file, oldMaterial);
						fieldRecordLogger.add("mpath_" + mid, oldPath, oldMaterial.getPath());
					}else{
						name = materialReader.getString(POST_TASK_MATERIAL_NAME);
					}
					
					oldMaterial.setDeleted(false);
					if(null != name){
						oldMaterial.setName(fieldRecordLogger.parseValue("mname_" + mid, oldMaterial.getName(), name));
					}
				}else{
					readMaterial(task, fileManager, newMaterials, materialReader);
				}
			}
			
			for(ImmediateTaskMaterial material : oldMaterials){
				material.setDeleted(true);
				newMaterials.add(material);
			}
			
			ImmediateTaskLog log;
			if(oldStatus != task.getStatus()){
				log = new ImmediateTaskLog();
			}else{
				ImmediateTaskStatusLog statuslog = new ImmediateTaskStatusLog();
				statuslog.setFromStatus(oldStatus);
				statuslog.setToStatus(task.getStatus());
				log = statuslog;
			}
			log.setContent(logContent);
			log.setCreateAt(now);
			log.setSource(task);
			log.operator(user);
			
			entityManager.persist(log);
			for(ImmediateTaskField fieldLog : fieldRecordLogger){
				fieldLog.setSource(task);
				fieldLog.setLogger(log);
				entityManager.persist(fieldLog);
			}
		}else{
			task = create(user, parameter);
			logContent = "[添加任务]";
			task.setRemark(parameter.getString(POST_TASK_REMARK));
			beforeSave(user, entityManager, parameter, task);
			entityManager.persist(task);
			afterSave(user, entityManager, parameter, task);
			//处理文件上传
			for(SubparameterReader materialReader : parameter.parameters(POST_TASK_MATERIAL)){
				readMaterial(task, fileManager, newMaterials, materialReader);
			}
			
			ImmediateTaskLog log = new ImmediateTaskLog();
			log.setContent(logContent);
			log.setCreateAt(now);
			log.setSource(task);
			log.operator(user);
			
			entityManager.persist(log);
		}
		
		for(ImmediateTaskMaterial material : newMaterials){
			if(material.isNew()){
				entityManager.persist(material);
			}else{
				entityManager.merge(material);
			}
		}
	}
	
	protected void readMaterial(T task, FileManager fileManager, List<ImmediateTaskMaterial> materials, SubparameterReader materialReader)throws Exception{
		ImmediateTaskMaterial material = new ImmediateTaskMaterial();
		material.setTask(task);
		
		MultipartFile file = materialReader.notEmptyFile(POST_TASK_MATERIAL_FILE, "必须上传附件！");
		String name = materialReader.getString(POST_TASK_MATERIAL_NAME, file.getOriginalFilename());
		material.setName(name);
		
		storageMaterial(fileManager, file, material);
		
		materials.add(material);
	}
	
	protected void storageMaterial(FileManager fileManager, MultipartFile file, ImmediateTaskMaterial material) throws Exception{
		StorageMultipartFile sfile = StorageFile.valueOf(SAVE_FILE_DIR, file);
		FileInfo ifile = fileManager.saveFile(sfile);
		material.setPath(ifile.toString());
	}
	
	protected ImmediateTaskMaterial findMaterial(List<ImmediateTaskMaterial> materials, Long mid){
		if(mid != null && mid > 0){
			Iterator<ImmediateTaskMaterial> iter = materials.iterator();
			while(iter.hasNext()){
				ImmediateTaskMaterial ma = iter.next();
				if(mid.equals(ma.getId())){
					iter.remove();
					return ma;
				}
			}
		}
		return null;
	}
	
	public ImmediateTaskEntity loadToShow(IOperator operator, EntityManager entityManager, ParameterReader parameter, Model model) {
		Long id = parameter.getLong(POST_TASK_ID);
		Assert.isTrue(null != id && id > 0, "参数[id]缺失，无法加载任务对象！");
		T entity = load(operator, parameter, entityManager, id);
		parseShow(operator, parameter, model, entity);
		
		model.addAttribute(VAR_TASK_INFO, entity);
		List<ImmediateTaskMaterial> materials = new ArrayList<>();
		for(ImmediateTaskMaterial tm : entity.getMaterials()){
			if(!tm.isDeleted()){
				materials.add(tm);
			}
		}
		model.addAttribute(VAR_TASK_MATERIALS, materials);
		//用来控制是否处于可编辑状态的
		model.addAttribute(STATUS_EDITABLE, checkEditable(entity));
		return entity;
	}
	
	public boolean checkEditable(T entity){
		Date now = new Date();
		if(now.after(entity.getEndAt())){
			//已经超时
			return false;
		}
		ImmediatePlanEntity plan = entity.getPlan();
		return  plan.getStatus().taskEditable()
				&& now.after(plan.getStartAt())
				&& now.before(plan.getExpireAt())
				&& entity.getStatus().editable();
	}
	
	protected void parseShow(IOperator operator, ParameterReader parameter, Model model, T task){}

	public ImmadiatePlanDefinition getDefinition() {
		return definition;
	}

	public abstract void export(IOperator user, ParameterReader parameter,
			HttpServletResponse response, ServletContext servletContext, EntityManager entityManager);
}
